package com.ics.cmsadmin.frame.init;

import com.ics.cmsadmin.frame.property.AppConfig;
import com.ics.cmsadmin.frame.core.annotation.Authorize;
import com.ics.cmsadmin.modules.auth.bean.SysAuthorize;
import com.ics.cmsadmin.modules.auth.bean.SysControllerAuthorize;
import com.ics.cmsadmin.frame.core.bean.TwoTupleBean;
import com.ics.cmsadmin.modules.auth.service.AuthorizeService;
import com.ics.cmsadmin.frame.utils.GsonUtils;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;

/**
 * 在spring启动后,初始化程序中所使用的权限码
 * Created by pc on 2017/10/7.
 */
@Component
@Log4j2
public class InitAuthorizeCallback implements Callable<String> {
    @Resource
    private AuthorizeService authorizeService;
    @Resource
    private ApplicationContext context;
    @Resource
    private AppConfig appConfig;

    @Override
    public String call() throws Exception {
        if (! appConfig.isInitAuthorize()){
            log.warn("==> 不初始化权限码");
            return AfterSpringStarted.TASK_DONE;
        }
        log.info("==> 开始初始化权限吗操作");
        TwoTupleBean<List<SysControllerAuthorize>, Set<SysAuthorize>> authorizeList =
                new TwoTupleBean<>(new ArrayList<>(), new HashSet<>());
        String[] controllers = context.getBeanNamesForAnnotation(Controller.class);
        if (ArrayUtils.isEmpty(controllers)){
            return AfterSpringStarted.TASK_DONE;
        }
        for (String controllerBean : controllers){
            configSingleController(controllerBean, authorizeList);
        }
        authorizeService.batchAddControllerAuthorize(authorizeList.one);
        authorizeService.batchAddAuthorizeInfo(new ArrayList<>(authorizeList.two));
        log.info("==> 初始化权限吗操作完毕");
        return AfterSpringStarted.TASK_DONE;
    }

    /**
     * 配置单个控制层方法
     * @param controllerBean
     */
    private void configSingleController(String controllerBean,
                                        TwoTupleBean<List<SysControllerAuthorize>, Set<SysAuthorize>> authorizeList) {
        Object controller = getTarget(context.getBean(controllerBean));
        Method[] methods = controller.getClass().getMethods();
        RequestMapping classRequestMapper = AnnotationUtils.findAnnotation(controller.getClass(), RequestMapping.class);
        String baseRequestPath = classRequestMapper == null ? "" : StringUtils.join(classRequestMapper.value(), ",");
        if (ArrayUtils.isEmpty(methods)){
            return;
        }
        for (Method method : methods){
            RequestMapping methodRequestMapper = AnnotationUtils.findAnnotation(method, RequestMapping.class);
            if (methodRequestMapper == null){
                continue;
            }
            String methodRequestPath = StringUtils.join(methodRequestMapper.value(), ",");
            SysControllerAuthorize controllerAuthorizeBean = new SysControllerAuthorize();
            controllerAuthorizeBean.setClassName(controller.getClass().getName());
            controllerAuthorizeBean.setRequestMapper(baseRequestPath + methodRequestPath);
            controllerAuthorizeBean.setMethodName(method.getName());
            Authorize authorize = method.getAnnotation(Authorize.class);
            if (authorize != null){
                controllerAuthorizeBean.setAuthCode(GsonUtils.toJson(AnnotationUtils.getAnnotationAttributes(authorize)));
                Set<SysAuthorize> authorizeInfoBeanSet = CollectionUtils.collate(Arrays.asList(authorize.value()), Arrays.asList(authorize.any()))
                        .stream().map(authorizeEnum -> {
                            SysAuthorize authorizeBean = new SysAuthorize();
                            authorizeBean.setAuthCode(authorizeEnum.name());
                            authorizeBean.setGroup(authorizeEnum.getGroup().name());
                            authorizeBean.setMenuId(authorizeEnum.getGroup().getMenuId());
                            authorizeBean.setType(authorizeEnum.getType().name());
                            authorizeBean.setDescription(authorizeEnum.getDescription());
                            return authorizeBean;
                        }).collect(Collectors.toSet());

                authorizeList.two.addAll(authorizeInfoBeanSet);
            }
            authorizeList.one.add(controllerAuthorizeBean);
        }
    }

    public static Object getTarget(Object beanInstance) {
        if (!AopUtils.isAopProxy(beanInstance)) {
            return beanInstance;
        } else if (AopUtils.isCglibProxy(beanInstance)) {
            try {
                Field h = beanInstance.getClass().getDeclaredField("CGLIB$CALLBACK_0");
                h.setAccessible(true);
                Object dynamicAdvisedInterceptor = h.get(beanInstance);

                Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
                advised.setAccessible(true);

                Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
                return target;
            } catch (Exception e) {
                log.error("获取目标类异常: {}", e);
            }
        }
        return null;

    }

}
